package rnxutils

import (
	"bufio"
	"bytes"
	"fmt"
	"gitee.com/ymofen/gobase"
	"gitee.com/ymofen/golog"
	"io"
	"os"
	"strings"
	"time"
)

type RinexHeader struct {
	MarkerName    string
	MarkerNO      string
	MarkerType    string
	RecNO         string
	RecType       string
	RecVersion    string
	AntennaNO     string
	AntennaType   string
	AntennaDelta  [3]float64
	Xyz           [3]float64
	FirstTime     time.Time
	LastTime      time.Time
	EndHeaderFlag int8

	// 返回false表示终止
	OnParseError func(line string, err error) bool
}

func (this *RinexHeader) processLine(idx int64, line []byte) bool {
	str := gobase.Trim(string(line))
	if strings.HasSuffix(str, "MARKER NAME") {
		str0 := gobase.ReplaceMultiSpace2One(str, " ")
		s1, _ := gobase.Split2Str(str0, " ")
		this.MarkerName = s1
	} else if strings.HasSuffix(str, "MARKER NUMBER") {
		str0 := gobase.ReplaceMultiSpace2One(str, " ")
		s1, _ := gobase.Split2Str(str0, " ")
		this.MarkerNO = s1
	} else if strs0, ok0 := SplitWithWidth(str, "TIME OF FIRST OBS"); ok0 {
		//   2024     7    20     0     0    0.0000000     GPS         TIME OF FIRST OBS
		for i := 0; i < len(strs0); i++ {
			f0 := gobase.StrToFloat64Def(strs0[i], -1)
			if f0 < 60 {
				strs0[i] = fmt.Sprintf("%.2d", int(f0))
			}
		}

		if len(strs0) < 6 {
			evt := this.OnParseError
			if evt != nil {
				return evt(str, fmt.Errorf("invalid TIME OF FIRST OBS"))
			}
			golog.Default().Warnf("invalid TIME OF FIRST OBS, %s, %s ", this.MarkerName, str)
			return true
		}
		this.FirstTime = time.Date(gobase.StrToIntDef(strs0[0], 0), time.Month(gobase.StrToIntDef(strs0[1], 0)), gobase.StrToIntDef(strs0[2], 0),
			gobase.StrToIntDef(strs0[3], 0), gobase.StrToIntDef(strs0[4], 0), 0, 0, time.UTC)
		this.FirstTime.Add(time.Duration(gobase.StrToFloat64Def(strs0[5], 0)*1000) * time.Millisecond)
	} else if strs0, ok0 := SplitWithWidth(str, "TIME OF LAST OBS"); ok0 {
		//   2024     7    20     0     0    0.0000000     GPS         TIME OF LAST OBS
		for i := 0; i < len(strs0); i++ {
			f0 := gobase.StrToFloat64Def(strs0[i], -1)
			if f0 < 60 {
				strs0[i] = fmt.Sprintf("%.2d", int(f0))
			}
		}

		if len(strs0) < 6 {
			evt := this.OnParseError
			if evt != nil {
				return evt(str, fmt.Errorf("invalid TIME OF LAST OBS"))
			}

			golog.Default().Warnf("invalid TIME OF LAST OBS, %s, %s ", this.MarkerName, str)
			return true
		}
		this.LastTime = time.Date(gobase.StrToIntDef(strs0[0], 0), time.Month(gobase.StrToIntDef(strs0[1], 0)), gobase.StrToIntDef(strs0[2], 0),
			gobase.StrToIntDef(strs0[3], 0), gobase.StrToIntDef(strs0[4], 0), 0, 0, time.UTC)
		this.LastTime.Add(time.Duration(gobase.StrToFloat64Def(strs0[5], 0)*1000) * time.Millisecond)
	} else if strs0, ok0 := SplitWithWidth(str, "REC # / TYPE / VERS", 20, 20, 20); ok0 {
		// 5818R40023          TRIMBLE ALLOY       5.45                REC # / TYPE / VERS
		this.RecNO = strs0[0]
		this.RecType = strs0[1]
		this.RecVersion = strs0[2]
	} else if strs0, ok0 := SplitWithWidth(str, "ANT # / TYPE", 20, 20); ok0 {
		// 10240011            LEIAR25.R3      LEIT                    ANT # / TYPE
		this.AntennaNO = strs0[0]
		this.AntennaType = strs0[1]
	} else if strs0, ok0 := SplitWithWidth(str, "APPROX POSITION XYZ", 14, 14, 14); ok0 {
		this.Xyz[0] = gobase.StrToFloat64Def(strs0[0], 0)
		this.Xyz[1] = gobase.StrToFloat64Def(strs0[1], 0)
		this.Xyz[2] = gobase.StrToFloat64Def(strs0[2], 0)
	} else if strs0, ok0 := SplitWithWidth(str, "ANTENNA: DELTA H/E/N", 14, 14, 14); ok0 {
		this.AntennaDelta[0] = gobase.StrToFloat64Def(strs0[0], 0)
		this.AntennaDelta[1] = gobase.StrToFloat64Def(strs0[1], 0)
		this.AntennaDelta[2] = gobase.StrToFloat64Def(strs0[2], 0)

	} else if strings.HasSuffix(str, "END OF HEADER") { // 读取完毕
		this.EndHeaderFlag = 1
		return false
	}
	return true
}

func (this *RinexHeader) Read(r io.Reader) error {
	br := bufio.NewReaderSize(r, 4096)
	index := int64(0)
	lineBuf := make([]byte, 0, 8192)
	for {
		tmpBuf, isPrefix, err := br.ReadLine()
		if err == io.EOF {
			if len(lineBuf) > 0 {
				this.processLine(index, lineBuf)
			}
			break
		}
		if err != nil {
			if len(lineBuf) > 0 {
				this.processLine(index, lineBuf)
			}
			return err
		}
		lineBuf = append(lineBuf, tmpBuf...)
		if !isPrefix {
			if !this.processLine(index, lineBuf) {
				break
			}
			index++
			lineBuf = lineBuf[:0]
		}
	}
	return nil
}

func (this *RinexHeader) ReadFromFile(fileName string) error {
	file, err := os.Open(fileName)
	if err != nil {
		return err
	}
	defer file.Close()
	return this.Read(file)
}

func (this *RinexHeader) ReadFromText(s string) error {
	r := bytes.NewReader([]byte(s))
	return this.Read(r)
}
